package com.meshnetics.gb.type.impl.additional;

import com.meshnetics.gb.type.DataValue;
import com.meshnetics.gb.type.impl.zcl.AbstractDataType;

import java.nio.ByteBuffer;

/**
 * Base class for immuteable objects. Has cached toString
 * represenattion and cached hash code.
 *
 * @author Yury Aserchev (yaserchev@meshnetics.com)
 *         Date: 01.08.2008 14:17:40
 */
public abstract class BaseImmutableObject extends BaseDataValue {
    /** Contains toString header of object. */
    private final String header;
    /** Contains cached String representation of object. */
    private String str;
    /** Contains cached hash code of object. */
    private Integer hash;
    /** Contains cached fields of object. */
    private DataValue[] values;

    protected BaseImmutableObject(final AbstractDataType type, final String header) {
        super(type);
        if (header == null) {
            throw new NullPointerException("parameter header is null");
        }
        this.header = header;
    }

    /**
     * Returns hash code of the object.
     * @return hash code of the object.
     */
    private int getHash() {
        return HelperImmutableObject.calculateHash(getCachedValues());
    }

    /**
     * Returns String representation of the object.
     * @return String representation of the object.
     */
    private String getString() {
        return HelperImmutableObject.toString(header, getCachedValues());
    }


    /**
     * Returns String representation of the object.
     * @return String representation of the object.
     */
    public final String toString() {
        if (str == null) {
            str = getString();
        }
        return str;
    }

    /**
     * Returns hash code of the object.
     * @return hash code of the object.
     */
    public final int hashCode() {
        if (hash == null) {
            hash = Integer.valueOf(getHash());
        }
        return hash.intValue();
    }

    /**
     * Performs comparison of objects. Returns true if objects are equal, false othewise.
     * @param o given object.
     * @return Returns true if objects are equal, false othewise.
     */
    public final boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        final BaseImmutableObject that = (BaseImmutableObject) o;
        return hashCode() == that.hashCode() &&
                HelperImmutableObject.isDataEqual(getCachedValues(), that.getCachedValues());
    }

    private DataValue[] getCachedValues() {
        if (values == null) {
            values = (DataValue[]) getValues().clone();
        }
        return values;
    }

    public final int getDataSize() {
        return HelperImmutableObject.calculateSize(getCachedValues());
    }

    public final void serialize(final ByteBuffer buffer) {
        HelperImmutableObject.serialize(buffer, getCachedValues());
    }

    protected abstract DataValue[] getValues();
}
